package com.wave.gate.netty;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.wave.gate.dto.FaceMessage;
import com.wave.gate.dto.FaceOperation;
import com.wave.gate.dto.GatePassRecord;
import com.wave.gate.enums.*;
import com.wave.gate.init.InitBaseInfo;
import com.wave.gate.mqtt.client.MqttSendClient;
import com.wave.gate.util.*;
import com.wave.gate.vo.GatePassSetVo;
import com.wave.gate.vo.GatePassWeightParamVo;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.io.IOException;
import java.math.BigDecimal;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.time.LocalTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import static com.wave.gate.util.GuavaCache.*;

@Slf4j
public class FaceServerHandler extends SimpleChannelInboundHandler<FaceMessage> {

    private MqttSendClient mqttSendClient;
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
    public FaceServerHandler(MqttSendClient mqttSendClient, ThreadPoolTaskExecutor threadPoolTaskExecutor){
        this.mqttSendClient = mqttSendClient;
        this.threadPoolTaskExecutor = threadPoolTaskExecutor;
    }

    public static Map<String, Channel> channels = new ConcurrentHashMap<>();

    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        InetSocketAddress socketAddress = (InetSocketAddress) ctx.channel().remoteAddress();
        log.info("客户端" + socketAddress.getAddress().getHostAddress() + "上线了");
        channels.put(socketAddress.getAddress().getHostAddress(), ctx.channel());
        log.info("*****人脸机列表****");
        log.info("在线人脸机数：" + channels.size());
        for (Map.Entry<String, Channel> client : channels.entrySet()) {
            log.info("人脸机,key:" + client.getKey() + ",value:" + client.getValue());
        }
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        log.info("{} 已经正常断开", ctx.channel());
        InetSocketAddress socketAddress = (InetSocketAddress) ctx.channel().remoteAddress();
        channels.remove(socketAddress.getAddress().getHostAddress());
        ctx.close();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        log.info("{} 已经异常断开，异常是{}", ctx.channel(), cause.getMessage());
        InetSocketAddress socketAddress = (InetSocketAddress) ctx.channel().remoteAddress();
        channels.remove(socketAddress.getAddress().getHostAddress());
        ctx.close();
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, FaceMessage faceMessage) {
        GatePassSetVo gatePassSetVo = GuavaCache.PASSSET_CACHE.getIfPresent("passsetipInfo");
        if(null == gatePassSetVo){
            log.error("获取本套设备ip集合失败,程序终止运行！！！");
            return;
        }

        //A门人脸机IP
        String faceAIp = gatePassSetVo.getInFaceIp();
        //B门人脸机IP
        String faceBIp = gatePassSetVo.getOutFaceIp();
        //A门闸机IP
        String gateAIp = gatePassSetVo.getInGateIp();
        //A门闸机端口
        int gateAPort = 502;
        //B门闸机IP
        String gateBIp = gatePassSetVo.getOutGateIp();
        //B门闸机端口
        int gateBPort = 502;

        //传送带
        Socket conveyer = null;
        Socket doorA = null;
        Socket doorB = null;
        String requestID = "";
        //供应商工衣用
        String AGkey = "";

        Channel channel = ctx.channel();
        Channel otherChannel = null;
        try {
            //收到数据后，立马给人脸机上锁
            sendFace(channels.get(faceAIp), requestID, "off", "核验中");
            sendFace(channels.get(faceBIp), requestID, "off", "核验中");

            //业务处理
            Integer workMode = DOOR_MODE_CACHE.getIfPresent(RedisKeyEnum.KEY_REMOTE_CONTROLL.getCode());
            if (workMode != 1) {
                log.info("非核验模式");
                return;
            }
            requestID = faceMessage.getRequestID();
            String deviceType = ("A1".equals(faceMessage.getDeviceType()) || "A2".equals(faceMessage.getDeviceType())) ? "in" : "out";

            //存入缓存
            DEVICETYPE_CACHE.put("deviceType", deviceType);

            //另一个人脸机
            otherChannel = "in".equals(deviceType) ? channels.get(faceBIp) : channels.get(faceAIp);

            GatePassRecord gatePassRecordDTO = getGatePassRecordDTO(faceMessage);
            gatePassRecordDTO.setDirection(deviceType);
            gatePassRecordDTO.setFacePassTime(new Date());
            //权限验证失败
            if ("N".equals(faceMessage.getResult())) {
                VoicePlayerUtil.voicePlay(1);
                log.info("语音：人脸核验失败，禁止通行-182");
                /*sendFace(channel, requestID, "on", "权限认证失败");
                sendFace(otherChannel, requestID, "on", "权限认证失败");*/
                return;
            }

            if (UserTypeEnum.USER_TYPE_5.getCode().equals(faceMessage.getEmpType())) {
                String camera = "入口";
                if("out".equals(deviceType)){
                    camera = "出口";
                }
                //供应商工衣缓存key
                AGkey = RedisKeyEnum.KEY_AI_GY.getCode() + ":" + camera;

                //供应商
                String modelPath = AI_CACHE.getIfPresent(AGkey);
                int count = 0;
                while (StrUtil.isEmpty(modelPath)) {
                    if (count > 5) {
                        break;
                    }
                    TimeUnit.MILLISECONDS.sleep(300);
                    modelPath = AI_CACHE.getIfPresent(AGkey);
                    count++;
                    log.info("count==" + count);
                }

                if (!StringUtils.isNoneEmpty(modelPath)) {
                    if (!"已穿访客工衣".equals(modelPath)) {
                        //未穿访客工衣
                        VoicePlayerUtil.voicePlay(15);
                        log.info("语音：未穿供应商马甲，禁止通行-194");
                        gatePassRecordDTO.setStatus("2");
                        gatePassRecordDTO.setAlarmType(ExceptionEnum.EXCEPTION_9.getCode());
                        gatePassRecordDTO.setAlarmTime(new Date());
                        sendGatePassRecord(gatePassRecordDTO);
                        return;
                    }
                }
            }
            //判断是否有尾随
            int aIalarm_fallow = readAIalarm();
            if (aIalarm_fallow > 0) {
                if ("in".equals(deviceType)) {
                    if (aIalarm_fallow == 101) {
                        //尾随-入口-门外警告
                        VoicePlayerUtil.voicePlay(18);
                        log.info("语音：门外警告-发现尾随，请退出-188");

                        gatePassRecordDTO.setStatus("2");
                        gatePassRecordDTO.setAlarmType(ExceptionEnum.EXCEPTION_11.getCode());
                        gatePassRecordDTO.setAlarmTime(new Date());
                        gatePassRecordDTO.setAiAlarmImgBase64(AI_CACHE.getIfPresent(RedisKeyEnum.KEY_AI_ALARM.getCode() + ":IMG"));
                        sendGatePassRecord(gatePassRecordDTO);
                       /* sendFace(channel, requestID, "on", "请尾随人员退出");
                        sendFace(otherChannel, requestID, "on", "请尾随人员退出");*/

                        return;
                    }
                } else {
                    if (aIalarm_fallow == 103) {
                        //尾随-出口-门外告警
                        VoicePlayerUtil.voicePlay(18);
                        log.info("语音：出口-门外警告-发现尾随，请退出-203");

                        gatePassRecordDTO.setStatus("2");
                        gatePassRecordDTO.setAlarmType(ExceptionEnum.EXCEPTION_11.getCode());
                        gatePassRecordDTO.setAlarmTime(new Date());
                        gatePassRecordDTO.setAiAlarmImgBase64(AI_CACHE.getIfPresent(RedisKeyEnum.KEY_AI_ALARM.getCode() + ":IMG"));
                        sendGatePassRecord(gatePassRecordDTO);
                        /*sendFace(channel, requestID, "on", "请尾随人员退出");
                        sendFace(otherChannel, requestID, "on", "请尾随人员退出");*/

                        return;
                    }
                }
            }

            //如果有人进入，那么插入redis一个标记,15s钟
            HASHUMAN_CACHE.put("hasHuman", 1);

            log.info("人脸识别成功后没有尾随存入ws缓存，以前的尾随播报语音就停止了。。。。。。。。。。");
            AI_WS_CACHE.put("ws","1");

            if ("in".equals(deviceType)) {
                doorA = new Socket(gateAIp, gateAPort);
                doorB = new Socket(gateBIp, gateBPort);
            } else {
                doorA = new Socket(gateBIp, gateAPort);
                doorB = new Socket(gateAIp, gateBPort);
            }
            //sendFace(channel, requestID, "off", "核验中");

            VoicePlayerUtil.voicePlay(9);
            log.info("语音：核验中-242");
            //给另一侧人脸机发1：通行中、请等待 加锁
            //sendFace(otherChannel, requestID, "off", "通行中，请等待");

            //是否有物品要传送
            long a1 = System.currentTimeMillis();
            Set<Double> deviceWeightData;
            if ("in".equals(deviceType)) {
                deviceWeightData = NorcoAPIUtil.getDeviceWeightData(1);
            } else {
                deviceWeightData = NorcoAPIUtil.getDeviceWeightData(2);
            }
            long a2 = System.currentTimeMillis();
            log.info("称重耗时:" + (a2 - a1));
            //是否有物品
            boolean hasGoods = false;
            if (deviceWeightData != null && deviceWeightData.size() > 0) {
                hasGoods = true;
                //有物品传送
                List<Double> weightList = new ArrayList<>(deviceWeightData);
                double min_weight = weightList.get(0);
                log.info("min_weight:" + min_weight);
                double max_weight = weightList.get(weightList.size() - 1);
                log.info("max_weight:" + max_weight);
                gatePassRecordDTO.setWeight(new BigDecimal(max_weight));
                //判断重量是否正常
                log.info("获取重量开始：" + LocalTime.now());
                GatePassWeightParamVo weightCorrect1 = checkWeight(min_weight);
                GatePassWeightParamVo weightCorrect2 = checkWeight(max_weight);
                log.info("获取重量结束：" + LocalTime.now());
                //校验称重信息
                if (weightCorrect1 == null || weightCorrect2 == null) {
                    VoicePlayerUtil.voicePlay(2);
                    log.info("语音：物品重量不正确，禁止通行-272");
                    sendFace(channel, requestID, "on", "物品重量不匹配");
                    //给另一侧人脸机发消息解锁
                    sendFace(otherChannel, requestID, "on", "");
                    gatePassRecordDTO.setStatus("2");
                    gatePassRecordDTO.setAlarmType(ExceptionEnum.EXCEPTION_1.getCode());
                    gatePassRecordDTO.setAlarmTime(new Date());
                    sendGatePassRecord(gatePassRecordDTO);
                    return;
                }
                //给PLC指令,传送物品到扫码位置
                log.info("给PLC指令,传送物品到扫码位置");
                conveyer = new Socket(gatePassSetVo.getPclIp(), 502);
                SocketClientUtil.sendData(conveyer, ConveyerEnum.CONVEYER_COMMAND_M6.getCode());
                log.info("一开始给传送带发送命令M6初始化");
                Thread.sleep(200);
                if ("in".equals(deviceType)) {
                    SocketClientUtil.sendData(conveyer, ConveyerEnum.CONVEYER_COMMAND_M0.getCode());
                    log.info("给传送带发送命令M0");
                } else {
                    SocketClientUtil.sendData(conveyer, ConveyerEnum.CONVEYER_COMMAND_M4.getCode());
                    log.info("给传送带发送命令M4");
                }
                //核验token二维码、手机二维码、称重信息、待检区人数
                //1:手机,2:token,3:手机+token
                Integer type = weightCorrect1.getType();
                log.info("重量组合type：" + type);
                //获取手机二维码
                String phone_QR_code = "";
                //获取token二维码
                String token_QR_code = "";
                if ("in".equals(deviceType)) {
                    if (type == 1 || type == 3) {
                        //获取手机二维码
                        phone_QR_code = NorcoAPIUtil.getDeviceData(1);
                    }
                    if (type == 2 || type == 3) {
                        //获取token二维码
                        token_QR_code = NorcoAPIUtil.getDeviceData(2);
                    }
                } else {
                    if (type == 1 || type == 3) {
                        //获取手机二维码
                        phone_QR_code = NorcoAPIUtil.getDeviceData(3);
                    }
                    if (type == 2 || type == 3) {
                        //获取token二维码
                        token_QR_code = NorcoAPIUtil.getDeviceData(4);
                    }
                }
                if (type == 1 || type == 3) {
                    if (StringUtils.isEmpty(phone_QR_code)) {
                        VoicePlayerUtil.voicePlay(3);
                        log.error("扫码拿不到手机二维码");
                        /*sendFace(channel, requestID, "on", "扫码拿不到手机二维码");
                        sendFace(otherChannel, requestID, "on", "扫码拿不到手机二维码");*/
                        if ("in".equals(deviceType)) {
                            SocketClientUtil.sendData(conveyer, ConveyerEnum.CONVEYER_COMMAND_M7.getCode());
                            log.info("给传送带发送命令M7");
                        } else {
                            SocketClientUtil.sendData(conveyer, ConveyerEnum.CONVEYER_COMMAND_M8.getCode());
                            log.info("给传送带发送命令M8");
                        }
                        return;
                    }
                }
                if (type == 2 || type == 3) {
                    if (StringUtils.isEmpty(token_QR_code)) {
                        VoicePlayerUtil.voicePlay(4);
                        log.error("扫码拿不到tokenid");
                        /*sendFace(channel, requestID, "on", "扫码拿不到tokenid");
                        sendFace(otherChannel, requestID, "on", "扫码拿不到tokenid");*/
                        if ("in".equals(deviceType)) {
                            SocketClientUtil.sendData(conveyer, ConveyerEnum.CONVEYER_COMMAND_M7.getCode());
                            log.info("给传送带发送命令M7");
                        } else {
                            SocketClientUtil.sendData(conveyer, ConveyerEnum.CONVEYER_COMMAND_M8.getCode());
                            log.info("给传送带发送命令M8");
                        }
                        return;
                    }
                }
                if (!checkWeightTwice(deviceType, channel, otherChannel, requestID, gatePassRecordDTO)) {
                    log.error("再次称重，物品重量不匹配-390");
                    if ("in".equals(deviceType)) {
                        SocketClientUtil.sendData(conveyer, ConveyerEnum.CONVEYER_COMMAND_M7.getCode());
                        log.info("传送带从A门退出物品-M7");
                    } else {
                        SocketClientUtil.sendData(conveyer, ConveyerEnum.CONVEYER_COMMAND_M8.getCode());
                        log.info("传送带从B门退出物品-M8");
                    }
                    return;
                }
                if (type == 1 || type == 3) {
                    if (!StringUtils.isEmpty(phone_QR_code)) {
                        log.info("扫码获取手机二维码:" + phone_QR_code);
                        gatePassRecordDTO.setPhoneQRcode(phone_QR_code);
                        String mobilePincode = faceMessage.getMobilePinCode() + ";" + faceMessage.getEmpNo();
                        log.info("人脸机返回的二维码和员工编号：" + mobilePincode);
                        //校验二维码
                        String[] mobilePincodeSplit = mobilePincode.split(";");
                        //校验手机二维码
                        boolean checkMobilePincode = false;
                        for (String s : mobilePincodeSplit) {
                            if (phone_QR_code.equalsIgnoreCase(s)) {
                                checkMobilePincode = true;
                                break;
                            }
                        }
                        if (!checkMobilePincode) {
                            VoicePlayerUtil.voicePlay(3);
                            log.info("语音：手机二维码验证失败，禁止通行-403");
                            //sendFace(channel, requestID, "on", "手机二维码不匹配");
                            if ("in".equals(deviceType)) {
                                SocketClientUtil.sendData(conveyer, ConveyerEnum.CONVEYER_COMMAND_M7.getCode());
                                log.info("传送带从A门退出物品-M7");
                            } else {
                                SocketClientUtil.sendData(conveyer, ConveyerEnum.CONVEYER_COMMAND_M8.getCode());
                                log.info("传送带从B门退出物品-M8");
                            }
                            //给另一侧人脸机发消息解锁
                            //sendFace(otherChannel, requestID, "on", "");
                            gatePassRecordDTO.setStatus("2");
                            gatePassRecordDTO.setAlarmType(ExceptionEnum.EXCEPTION_2.getCode());
                            gatePassRecordDTO.setAlarmTime(new Date());
                            sendGatePassRecord(gatePassRecordDTO);
                            return;
                        }
                    }
                }
                if (type == 2 || type == 3) {
                    if (!StringUtils.isEmpty(token_QR_code)) {
                        log.info("扫码获取token二维码:" + token_QR_code);
                        gatePassRecordDTO.setTokenQRCode(token_QR_code);
                        //校验token二维码
                        boolean checkTokenID = false;
                        String tokenID = faceMessage.getTokenID();
                        log.info("人脸机返回的tokenID：" + tokenID);
                        if (!token_QR_code.equalsIgnoreCase(tokenID)) {
                            VoicePlayerUtil.voicePlay(4);
                            log.info("语音“令牌二维码验证失败，禁止通行-431");
                            //sendFace(channel, requestID, "on", "token二维码不匹配");
                            if ("in".equals(deviceType)) {
                                SocketClientUtil.sendData(conveyer, ConveyerEnum.CONVEYER_COMMAND_M7.getCode());
                                log.info("传送带从A门退出物品-M7");
                            } else {
                                SocketClientUtil.sendData(conveyer, ConveyerEnum.CONVEYER_COMMAND_M8.getCode());
                                log.info("传送带从B门退出物品-M8");
                            }
                            //给另一侧人脸机发消息解锁
                            //sendFace(otherChannel, requestID, "on", "");
                            gatePassRecordDTO.setStatus("2");
                            gatePassRecordDTO.setAlarmType(ExceptionEnum.EXCEPTION_3.getCode());
                            gatePassRecordDTO.setAlarmTime(new Date());
                            sendGatePassRecord(gatePassRecordDTO);
                            return;
                        }
                    }
                }
            }
            //提示：验证通过请通行！
            //A门闸机打开，通行人进入
            log.info("进口闸机打开，通行人进入");
            sendCommandToDoor(doorA, DoorCommandEnum.doorCommand6.getCode());
            gatePassRecordDTO.setGateOpenTime1(new Date());
            log.info("doorA: 01：常关；11：进向自由、出向自由-443");
            VoicePlayerUtil.voicePlay(10);
            log.info("语音：核验通过，请通行-456");

            //清空红外尾随数据
            NorcoAPIUtil.gpiohwMap.clear();

            if ("in".equals(deviceType)) {
                //设置尾随红外开始监听
                NorcoAPIUtil.gpiohwMap.put(InitBaseInfo.ipAddress + ":gpio:ws:124", 1);
                //sendFace(otherChannel, requestID, "off", "通行中，请等待");
                //sendFace(channel, requestID, "off", "核验通过，请通行");
            } else {
                //设置尾随红外开始监听
                NorcoAPIUtil.gpiohwMap.put(InitBaseInfo.ipAddress + ":gpio:ws:125", 1);
                //sendFace(otherChannel, requestID, "off", "通行中，请等待");
                //sendFace(channel, requestID, "off", "核验通过，请通行");
            }
            if (hasGoods) {
                //给PLC指令传送物品到B门暂停位置
                log.info("给PLC指令传送物品到B门暂停位置-457");
                if ("in".equals(deviceType)) {
                    SocketClientUtil.sendData(conveyer, ConveyerEnum.CONVEYER_COMMAND_M1.getCode());
                    log.info("给传送带发送命令M1-460");
                } else {
                    SocketClientUtil.sendData(conveyer, ConveyerEnum.CONVEYER_COMMAND_M5.getCode());
                    log.info("给传送带发送命令M5-463");
                }
            }
            //入口内侧红外感应检测到信号
            long beginTime = System.currentTimeMillis();
            String enterFlag = "";
            while (!"IN".equals(readInfraredIn(doorA))) {
                long currentTime = System.currentTimeMillis();
                long diff = (currentTime - beginTime) / 1000;
                if (diff > 5) {
                    log.error("超时未进去（5秒）");
                    /*sendCommandToDoor(doorA, DoorCommandEnum.doorCommand1.getCode());
                    log.info("doorA: 01：常关；22：进向禁止、出向禁止-475");*/
                    Thread.sleep(1000);
                    VoicePlayerUtil.voicePlay(16);
                    log.info("语音：超时未进入-482");
                    enterFlag = "NOT_IN";
                    break;
                }
            }
            Thread.sleep(800);
            sendCommandToDoor(doorA, DoorCommandEnum.doorCommand1.getCode());
            Thread.sleep(50);
            sendCommandToDoor(doorA, DoorCommandEnum.doorCommand5.getCode());
            log.info("doorA: 01：常关；12：进向禁止、出向自由-486");
            if ("NOT_IN".equals(enterFlag)) {
                /*sendFace(channel, requestID, "on", "未进入通道");
                sendFace(otherChannel, requestID, "on", "未进入通道");*/
                sendCommandToDoor(doorB, DoorCommandEnum.doorCommand1.getCode());
                log.info("doorB: 01：常关；22：进向禁止、出向禁止-489");
                Thread.sleep(1000);
                /*sendCommandToDoor(doorA, DoorCommandEnum.doorCommand1.getCode());
                log.info("doorA: 01：常关；22：进向禁止、出向禁止-492");*/
                if (hasGoods) {
                    SocketClientUtil.sendData(conveyer, ConveyerEnum.CONVEYER_COMMAND_M3.getCode());
                    log.info("给传送带发送命令M3-495");
                    //超时未进入，切有物品时，物品需要退出来才能扫脸，所以延迟两三秒解锁，等待物品退出来
                    Thread.sleep(3500);
                }
                //设置尾随红外结束监听
                if ("in".equals(deviceType)) {
                    NorcoAPIUtil.gpiohwMap.put(InitBaseInfo.ipAddress + ":gpio:ws:124", 2);
                } else {
                    NorcoAPIUtil.gpiohwMap.put(InitBaseInfo.ipAddress + ":gpio:ws:124", 2);
                }

                return;
            }

            //sendFace(otherChannel, requestID, "off", "通行中，请等待");

            sendCommandToDoor(doorB, DoorCommandEnum.doorCommand5.getCode());
            log.info("doorB: 01：常关；11：进向禁止、出向自由");

            //设置尾随红外结束监听
            int hwws = 1;
            log.info("尾随结果：{}", NorcoAPIUtil.gpiohwMap);

            if ("in".equals(deviceType)) {
                NorcoAPIUtil.gpiohwMap.put(InitBaseInfo.ipAddress + ":gpio:ws:124", 2);
                if (NorcoAPIUtil.gpiohwMap.get(InitBaseInfo.ipAddress + ":gpio:124") != null &&
                        NorcoAPIUtil.gpiohwMap.get(InitBaseInfo.ipAddress + ":gpio:124") > 1) {
                    hwws = 2;
                    log.info("尾随结果hwws：{}", hwws);
                }
            } else {
                NorcoAPIUtil.gpiohwMap.put(InitBaseInfo.ipAddress + ":gpio:ws:125", 2);

                if (NorcoAPIUtil.gpiohwMap.get(InitBaseInfo.ipAddress + ":gpio:125") != null &&
                        NorcoAPIUtil.gpiohwMap.get(InitBaseInfo.ipAddress + ":gpio:125") > 1) {
                    hwws = 2;
                    log.info("尾随结果hwws：{}", hwws);
                }
            }


            boolean warnFlag = false;
            int passRet = 0;
            //出口内侧红外感应检测到信号
            while (!"IN".equals(readInfraredIn(doorB))) {
                if ("OUT".equals(readInfraredOut(doorA))) {
                    //人从A门退出
                    //B门进出向禁止
                    sendCommandToDoor(doorB, DoorCommandEnum.doorCommand1.getCode());
                    log.info("doorB: 01：常关；22：进向禁止、出向禁止-473");
                    Thread.sleep(1000);
                    //A门进出向禁止
                    sendCommandToDoor(doorA, DoorCommandEnum.doorCommand1.getCode());
                    log.info("doorA: 01：常关；22：进向禁止、出向禁止-477");
                    passRet = 1;
                    /*sendFace(channel, requestID, "on", "");
                    sendFace(otherChannel, requestID, "on", "");*/
                    if (hasGoods) {
                        SocketClientUtil.sendData(conveyer, ConveyerEnum.CONVEYER_COMMAND_M3.getCode());
                        log.info("给传送带发送命令M3-482");
                    }
                    break;
                }
                long currentTime = System.currentTimeMillis();
                long diff = (currentTime - beginTime) / 1000;
                if (diff > 15) {
                    //超时（15秒）未出去
                    log.error("超时（15秒）未通过B门-502");
                    enterFlag = "NOT_OUT";
                    if (hasGoods) {
                        SocketClientUtil.sendData(conveyer, ConveyerEnum.CONVEYER_COMMAND_M3.getCode());
                        log.info("给传送带发送命令M3-539");
                    }
                    VoicePlayerUtil.voicePlay(13);
                    log.info("语音：请勿在通道内滞留-515");
                    break;
                }
                //读取金属探测结果
                int metal_detect = NorcoAPIUtil.mNorcoAPI.hwctrl_gpioRead(NorcoAPIUtil.gpioNumber_metal_detect);
                if (metal_detect > 0) {//有金属
                    //A/B门闸机打开，让通行人出去
                    VoicePlayerUtil.voicePlay(8);
                    log.info("语音：违规携带金属物品，禁止通行-550");
                }
                //从redis读取AI报警结果
                int aIalarmRet = readAIalarm();
                boolean realAIalarm = false;
                if (aIalarmRet > 0) {
                    log.error("AI告警(599)：" + aIalarmRet);
                    //尾随-中间区域，需要告警
                    if (aIalarmRet == 102 || aIalarmRet == 104) {
                        aIalarmRet = 5;
                    }
                    if ("in".equals(deviceType)) {
                        if (aIalarmRet == 101) {
                            aIalarmRet = 5;
                            realAIalarm = true;
                        }
                    } else {
                        if (aIalarmRet == 103) {
                            aIalarmRet = 5;
                            realAIalarm = true;
                        }
                    }
                    if (aIalarmRet != 6) {
                        if ("in".equals(deviceType)) {
                            if (aIalarmRet != 103) {
                                realAIalarm = true;
                                VoicePlayerUtil.voicePlay(aIalarmRet);
                                log.error("AI告警-624，aIalarmRet" + aIalarmRet);
                            }
                        } else {
                            if (aIalarmRet != 101) {
                                realAIalarm = true;
                                VoicePlayerUtil.voicePlay(aIalarmRet);
                                log.error("AI告警-630，aIalarmRet" + aIalarmRet);
                            }
                        }
                    }
                    log.info("634-aIalarmRet:" + aIalarmRet + ",realAIalarm:" + realAIalarm);
                }
                //有红外尾随感应
                if (hwws == 2 && passRet == 0) {
                    if (aIalarmRet != 5) {//防止语音告警两次
                        VoicePlayerUtil.voicePlay(5);
                    }
                    aIalarmRet = 5;
                    realAIalarm = true;

                    log.info("红外尾随感应-633");
                }
                //有金属、AI告警的情况控制闸机、传送带
                if (metal_detect > 0 || (aIalarmRet > 0 && realAIalarm)) {
                    //B门进出向禁止
                    sendCommandToDoor(doorB, DoorCommandEnum.doorCommand1.getCode());
                    log.info("doorB: 01：常关；22：进向禁止、出向禁止-598");
                    if (aIalarmRet == 5) {//尾随
                        //A门常关、出向自由、进向禁止
                        sendCommandToDoor(doorA, DoorCommandEnum.doorCommand5.getCode());
                    } else {
                        //A门常开、出向自由、进向禁止
                        sendCommandToDoor(doorA, DoorCommandEnum.doorCommand2.getCode());
                    }
                    log.info("doorA: 01：常关；12：进向禁止、出向自由-601");
                    if (hasGoods) {
                        SocketClientUtil.sendData(conveyer, ConveyerEnum.CONVEYER_COMMAND_M3.getCode());
                        log.info("给传送带发送命令M3-620");
                    }
                    //检测是否退出通道
                    beginTime = System.currentTimeMillis();
                    while (!"OUT".equals(readInfraredOut(doorA))) {
                        currentTime = System.currentTimeMillis();
                        diff = (currentTime - beginTime) / 1000;
                        if (diff > 15) {
                            sendFace(channel, requestID, "off", "");
                            sendFace(otherChannel, requestID, "off", "");
                            //超时未出来
                            VoicePlayerUtil.voicePlay(12);
                            log.info("AI/金属探测门告警，超时15秒未出来，语音：请退出通道-597");
                            enterFlag = "NOT_OUT";
                            break;
                        }
                        if ("OUT".equals(readInfraredOut(doorB))) {
                            //闸机为了防止夹住脚，就算红灯也会让人通过
                            //B门进出向禁止
                            sendCommandToDoor(doorB, DoorCommandEnum.doorCommand1.getCode());
                            log.info("doorB: 01：常关；22：进向禁止、出向禁止-621");
                            //A门进出向禁止
                            sendCommandToDoor(doorA, DoorCommandEnum.doorCommand1.getCode());
                            log.info("doorA: 01：常关；22：进向禁止、出向禁止-625");
                            /*sendFace(channel, requestID, "on", "已离开通道");
                            sendFace(otherChannel, requestID, "on", "已离开通道");*/
                            break;
                        }
                    }

                    sendCommandToDoor(doorB, DoorCommandEnum.doorCommand1.getCode());
                    log.info("doorB: 01：常关；22：进向禁止、出向禁止-643");
                    Thread.sleep(1000);
                    if (aIalarmRet == 5) {//尾随
                        sendCommandToDoor(doorA, DoorCommandEnum.doorCommand5.getCode());
                        log.info("doorA: 01：常关；12：进向禁止、出向自由-639");
                    } else {
                        sendCommandToDoor(doorA, DoorCommandEnum.doorCommand1.getCode());
                        log.info("doorA: 01：常关；22：进向禁止、出向禁止-642");
                    }
                    warnFlag = true;
                    gatePassRecordDTO.setStatus("2");
                    if (aIalarmRet == 5) {
                        gatePassRecordDTO.setAlarmType(ExceptionEnum.EXCEPTION_10.getCode());
                    } else if (aIalarmRet == 6) {
                        gatePassRecordDTO.setAlarmType(ExceptionEnum.EXCEPTION_6.getCode());
                    } else if (aIalarmRet == 7) {
                        gatePassRecordDTO.setAlarmType(ExceptionEnum.EXCEPTION_7.getCode());
                    } else if (aIalarmRet == 8) {
                        gatePassRecordDTO.setAlarmType(ExceptionEnum.EXCEPTION_8.getCode());
                    } else if (aIalarmRet == 14) {
                        gatePassRecordDTO.setAlarmType(ExceptionEnum.EXCEPTION_5.getCode());
                    }
                    if (metal_detect > 0) {
                        gatePassRecordDTO.setAlarmType(ExceptionEnum.EXCEPTION_4.getCode());
                    }
                    gatePassRecordDTO.setAlarmTime(new Date());
                    gatePassRecordDTO.setAiAlarmImgBase64(AI_CACHE.getIfPresent(RedisKeyEnum.KEY_AI_ALARM.getCode() + ":IMG"));
                    sendGatePassRecord(gatePassRecordDTO);
                    break;
                }
            }
            gatePassRecordDTO.setGateOpenTime2(new Date());
            gatePassRecordDTO.setCostTime((gatePassRecordDTO.getGateOpenTime2().getTime() - gatePassRecordDTO.getGateOpenTime1().getTime()) / 1000);
            if (passRet == 1) {
                //人直接从A门通过，未通过B门
                return;
            }
            log.info("enterFlag-658: " + enterFlag);
            if ("NOT_OUT".equals(enterFlag)) {
                log.info("超时未通过B门，A门出向自由、进向禁止，B门进出向禁止");
                //B门进出向禁止
                sendCommandToDoor(doorB, DoorCommandEnum.doorCommand1.getCode());
                log.info("doorB: 01：常关；22：进向禁止、出向禁止-679");
                //A门出向自由、进向禁止
                sendCommandToDoor(doorA, DoorCommandEnum.doorCommand2.getCode());
                log.info("doorA: 00：常开；12：进向禁止、出向自由-682");
                int i = 0;
                while (!"OUT".equals(readInfraredOut(doorA))) {
                    Thread.sleep(100);
                    //以下注释的几行是人脸机通过手动按钮进行解锁时，仍然扫不了脸的bug修复，如果后期提到了这个bug，打开测试没问题在发包
                                    /*String faceReset = redisCache.getCacheObject("faceReset");
                                    if("1".equals(faceReset)) {
                                        redisCache.deleteObject("faceReset");
                                        i++;
                                        break;
                                    }*/
                }
                                /*if(i>0){
                                    return;
                                }*/
                sendCommandToDoor(doorB, DoorCommandEnum.doorCommand1.getCode());
                log.info("doorB: 01：常关；22：进向禁止、出向禁止-687");
                Thread.sleep(1000);
                sendCommandToDoor(doorA, DoorCommandEnum.doorCommand1.getCode());
                log.info("doorA: 01：常关；22：进向禁止、出向禁止-690");
                sendFace(channel, requestID, "on", "");
                sendFace(otherChannel, requestID, "on", "");
                return;
            }
            //有告警
            if (warnFlag) {
                sendFace(otherChannel, requestID, "on", "");
                log.info("给B人脸机发送：3（有物品）-695");
                sendFace(channel, requestID, "on", "");
                log.info("给A人脸机发送：3（有物品）-697");
                return;
            }
            //红外感应到人，既没有AI告警，也没有探测到金属
            //外侧红外感应检测到信号
            if (hasGoods) {
                SocketClientUtil.sendData(conveyer, ConveyerEnum.CONVEYER_COMMAND_M2.getCode());
                log.info("给传送带发送命令M2-707");
            }
            beginTime = System.currentTimeMillis();
            while (!"OUT".equals(readInfraredOut(doorB))) {
                long currentTime = System.currentTimeMillis();
                long diff = (currentTime - beginTime) / 1000;
                if (diff > 15) {
                    //超时未出来
                    VoicePlayerUtil.voicePlay(13);
                    log.info("语音：请勿在通道内滞留-666");
                    break;
                }

            }
            VoicePlayerUtil.voicePlay(17);
            log.info("语音:一路顺风-762");

            sendCommandToDoor(doorA, DoorCommandEnum.doorCommand1.getCode());
            log.info("doorA: 01：常关；22：进向禁止、出向禁止");
            Thread.sleep(1000);
            sendCommandToDoor(doorB, DoorCommandEnum.doorCommand1.getCode());
            log.info("doorB: 01：常关；22：进向禁止、出向禁止-732");
            sendFace(channel, requestID, "on", "已离开通道");
            sendFace(otherChannel, requestID, "on", "已离开通道");
            //closeSocket(doorA, doorB, conveyer);
            gatePassRecordDTO.setStatus("1");
            sendGatePassRecord(gatePassRecordDTO);

            FaceEncoder.m_num = 0;
        } catch (Exception e) {
            log.error("通行异常: {}", e.getMessage(), e);
        } finally {
            try {
                //防止人脸机重复发送扫脸数据到工控机
                GuavaCache.FACE_CACHE.put(faceMessage.getEmpNo() + faceMessage.getDeviceType(), "Y");

                //清除5s内校验AI识别的供应商马甲的缓存
                CHECK_MODELPATH_CACHE.invalidateAll();
                //删除缓存中有人的标记
                HASHUMAN_CACHE.invalidateAll();
                //删除ai告警缓存
                AI_CACHE.invalidateAll();
                //清空红外尾随数据
                NorcoAPIUtil.gpiohwMap.clear();
                closeSocket(doorA, doorB, conveyer);

                Thread.sleep(100);
                sendFace(channel, requestID, "on", "finally已离开通道");
                sendFace(otherChannel, requestID, "on", "finally已离开通道");

            } catch (Exception e) {
                log.error("处理通行流程后事宜异常: {}", e.getMessage());
            }

        }
    }

    public static void sendFace(Channel channel, String requestID, String doType, String msg) {
        try {
            FaceOperation faceOperation = new FaceOperation();
            faceOperation.setRequestID(requestID);
            faceOperation.setDeviceIP(InitBaseInfo.ipAddress);
            faceOperation.setDoType(doType);
            faceOperation.setDealTime(DateUtil.formatDateTime(new Date()));
            faceOperation.setMessage(msg);
            channel.writeAndFlush(faceOperation);
        } catch (Exception e) {
            log.error("操作人脸机开锁/解锁出现错误，人脸机ip：{}, error:{}", channel.remoteAddress(), e.getMessage());
        }


    }


    /**
     * 初始化PassRecordDTO
     *
     * @param faceRecognitionDTO 人脸机数据
     * @return GatePassRecordDTO
     */
    private GatePassRecord getGatePassRecordDTO(FaceMessage faceRecognitionDTO) {
        GatePassRecord gatePassRecordDTO = new GatePassRecord();
        gatePassRecordDTO.setPassTime(new Date());
        gatePassRecordDTO.setJobNo(faceRecognitionDTO.getEmpNo());
        gatePassRecordDTO.setUserName(faceRecognitionDTO.getEmpName());
        gatePassRecordDTO.setUserType(faceRecognitionDTO.getEmpType());
        gatePassRecordDTO.setPhoneNum(faceRecognitionDTO.getMobilePinCode());
        gatePassRecordDTO.setTokenNum(faceRecognitionDTO.getTokenID());
        gatePassRecordDTO.setDirection(faceRecognitionDTO.getDeviceType());
        gatePassRecordDTO.setIpAddress(InitBaseInfo.ipAddress);
        //通道信息
        GatePassSetVo gatePassSetVo = GuavaCache.PASSSET_CACHE.getIfPresent("passsetipInfo");
        if(null!=gatePassSetVo){
            gatePassRecordDTO.setThoroughfare(gatePassSetVo.getName());
            gatePassRecordDTO.setGatePassSetId(gatePassSetVo.getId());
            gatePassRecordDTO.setEntrance(gatePassSetVo.getOutInName());
            gatePassRecordDTO.setGatePassOutInId(gatePassSetVo.getGatePassOutInId());
        }
        return gatePassRecordDTO;
    }

    /**
     * 读取AI告警结果
     */
    private int readAIalarm() {
        int aiAlarmFlag = 0;
        String modelPath = AI_CACHE.getIfPresent(RedisKeyEnum.KEY_AI_ALARM.getCode());
        if (modelPath != null) {
            switch (modelPath) {
                case "尾随-入口-门外告警":
                    aiAlarmFlag = 101;
                    break;
                case "尾随-入口-门内告警":
                    aiAlarmFlag = 102;
                    break;
                case "尾随-出口-门外告警":
                    aiAlarmFlag = 103;
                    break;
                case "尾随-出口-门内告警":
                    aiAlarmFlag = 104;
                    break;
                case "跨越":
                    aiAlarmFlag = 6;
                    break;
                case "弯腰":
                    aiAlarmFlag = 7;
                    break;
                case "下蹲":
                    aiAlarmFlag = 7;
                    break;
                case "举手过肩":
                    aiAlarmFlag = 14;
                default:
                    break;
            }
        }
        return aiAlarmFlag;
    }

    /**
     * 给闸机发送命令
     *
     * @param door    闸机
     * @param command 命令
     * @return 执行结果
     */
    private String sendCommandToDoor(Socket door, String command) {
        String str = SocketClientUtil.sendData(door, command);
        return str;
    }

    /**
     * 红外感应是否有人进入通道
     *
     * @param infrared 红外感应
     * @return 红外感应结果
     */
    public String readInfraredIn(Socket infrared) {
        String str = SocketClientUtil.sendData(infrared, "000006000006010303940008");
        //log.info("红外感应结果：" + str);
        String[] strArr = str.split(",");
        String lastStr = strArr[strArr.length - 1];
        lastStr = lastStr.replace("0x", "").trim();
        if (!"00".equals(lastStr)) {
            //log.info("十六进制：" + lastStr);
            //十六进制转成十进制
            int result_16 = Integer.parseInt(lastStr, 16);
            //log.info("十进制：" + result_16);
            //十进制转成二进制
            String result_2 = Integer.toBinaryString(result_16);
            //log.info("二进制：" + result_2);
            //获取第1位
            if ("1".equals(result_2.substring(result_2.length() - 1, result_2.length()))) {
                //有人进入
                log.info("有人进入-1092");
                return "IN";
            }
        }
        return "";
    }

    /**
     * 红外感应是否有人离开通道
     *
     * @param infrared 红外感应
     * @return 红外感应结果
     */
    public String readInfraredOut(Socket infrared) {
        String str = SocketClientUtil.sendData(infrared, "000006000006010303940008");
        //og.info("红外感应结果：" + str);
        String[] strArr = str.split(",");
        String lastStr = strArr[strArr.length - 1];
        lastStr = lastStr.replace("0x", "").trim();
        if (!"00".equals(lastStr)) {
            //log.info("十六进制：" + lastStr);
            //十六进制转成十进制
            int result_16 = Integer.parseInt(lastStr, 16);
            //log.info("十进制：" + result_16);
            //十进制转成二进制
            String result_2 = Integer.toBinaryString(result_16);
            //log.info("二进制：" + result_2);
            if (result_2.length() >= 8) {
                //获取倒数第8位
                String reverse_8 = result_2.substring((result_2.length() - 8), (result_2.length() - 7));
                if ("1".equals(reverse_8)) {
                    //有人出去
                    log.info("有人出去");
                    return "OUT";
                }
            }
        }
        return "";
    }

    /**
     * 校验重量
     *
     * @param weight 重量
     * @return 重量
     */
    private GatePassWeightParamVo checkWeight(double weight) {
        List<GatePassWeightParamVo> mobileTokenweightList = MOBILE_TOKEN_WEIGHTLIST_CACHE.getIfPresent("mobileTokenweightList");
        for (GatePassWeightParamVo gatePassWeightParamVo : mobileTokenweightList) {
            if (weight >= gatePassWeightParamVo.getWeightMin() && weight <= gatePassWeightParamVo.getWeightMax()) {
                return gatePassWeightParamVo;
            }
        }
        return null;
    }

    /**
     * 向后台发送通行记录
     *
     * @param gatePassRecordDTO 通行记录
     */
    void sendGatePassRecord(GatePassRecord gatePassRecordDTO) {
        threadPoolTaskExecutor.execute(() -> {
            log.info("sendGatePassRecord 异步发送mqtt通行记录");
            mqttSendClient.publish(false, "wave-gate:passRecord", JSON.toJSONString(gatePassRecordDTO));
        });
    }

    /**
     * 二次校验重量
     *
     * @param deviceType
     * @return
     * @throws InterruptedException
     */
    private boolean checkWeightTwice(String deviceType, Channel channel, Channel otherChannel,
                                     String requestID, GatePassRecord gatePassRecordDTO) throws InterruptedException {
        boolean ret = true;
        Set<Double> deviceWeightData;
        if ("in".equals(deviceType)) {
            deviceWeightData = NorcoAPIUtil.getDeviceWeightData(1);
        } else {
            deviceWeightData = NorcoAPIUtil.getDeviceWeightData(2);
        }
        //有物品传送
        List<Double> weightList = new ArrayList<>(deviceWeightData);
        double min_weight = weightList.get(0);
        log.info("min_weight-923:" + min_weight);
        double max_weight = weightList.get(weightList.size() - 1);
        log.info("max_weight-925:" + max_weight);
        //判断重量是否正常
        log.info("获取重量开始-927：" + LocalTime.now());
        GatePassWeightParamVo weightCorrect1 = checkWeight(min_weight);
        GatePassWeightParamVo weightCorrect2 = checkWeight(max_weight);
        log.info("获取重量结束-930：" + LocalTime.now());
        //校验称重信息
        if (weightCorrect1 == null || weightCorrect2 == null) {
            VoicePlayerUtil.voicePlay(2);
            log.info("语音：物品重量不正确，禁止通行-934");
            sendFace(channel, requestID, "on", "物品重量不匹配");
            //给另一侧人脸机发消息解锁
            sendFace(otherChannel, requestID, "on", "");
            gatePassRecordDTO.setStatus("2");
            gatePassRecordDTO.setAlarmType(ExceptionEnum.EXCEPTION_1.getCode());
            gatePassRecordDTO.setAlarmTime(new Date());
            sendGatePassRecord(gatePassRecordDTO);
            ret = false;
        }
        return ret;
    }

    /**
     * 关闭socket
     *
     * @param doorA    A门
     * @param doorB    B门
     * @param conveyer 传送带
     */
    private void closeSocket(Socket doorA, Socket doorB, Socket conveyer) {
        log.info("closeSocket begin");
        try {
            if (conveyer != null) {
                conveyer.close();
            }
        } catch (IOException e) {
            log.error("conveyer close() error: [{}]", e.getMessage(), e);
        }
        try {
            if (doorA != null) {
                doorA.close();
            }
        } catch (IOException e) {
            log.error("doorA close() error: [{}]", e.getMessage(), e);
        }
        try {
            if (doorB != null) {
                doorB.close();
            }
        } catch (IOException e) {
            log.error("doorB close() error: [{}]", e.getMessage(), e);
        }
        log.info("closeSocket end");
    }
}